﻿/**
 * 
 * Copyright 2009, 2010 Mike Jamer [email: mikejamer@gmail.com ]
 * 
 * This File is part of Moose IRC
 * 
 * MooseIRC is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * MooseIRC is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with MooseIRC.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using moose.irc;

namespace moose.irc.client
{
    /// <summary>
    /// Status Control Encapsuates an IRC Client object
    /// </summary>
    public partial class StatusControl : UserControl
    {

        #region [Local Attribtues]

        /// <summary>
        /// Irc Client
        /// </summary>
        private IrcClient m_client;

        /// <summary>
        /// Queue Handles Events
        /// </summary>
        private volatile LinkedList<IrcEvent> m_llEventsQueue;

        /// <summary>
        /// Timer used to process queued events
        /// </summary>
        private Timer m_tmrQueueProcessor;

        /// <summary>
        /// Collection of joined channels
        /// </summary>
        private Dictionary<string, ChannelForm> m_dictChannels;

        #endregion

        #region [Construction]

        /// <summary>
        /// Intiailize Status Window
        /// </summary>
        public StatusControl()
        {
            InitializeComponent();
            m_client = new IrcClient();
            m_llEventsQueue = new LinkedList<IrcEvent>();
            m_tmrQueueProcessor = new Timer();
            m_tmrQueueProcessor.Tick += onQueueProcessorTick;

            m_client.Event += onIrcEvent;
            m_client.StatusMessage += onStatusMessage;
            m_client.ActiveMessage += onActiveMessage;

            m_client.ClientSettings[IrcClientSettings.NICKNAME] = "mikeyj";
            m_client.ClientSettings[IrcClientSettings.USER_NAME] = "mike";
            m_client.ClientSettings[IrcClientSettings.REAL_NAME] = "mike jamer";

            start_event_processor();

            m_dictChannels = new Dictionary<string, ChannelForm>();

            return;
        }

        #endregion

        #region [Event Handlers]

        /// <summary>
        /// Handles IRC Events, by queueing them
        /// </summary>
        /// <param name="irc_event">irc event to queue</param>
        private void onIrcEvent(IrcEvent irc_event)
        {
            lock (m_llEventsQueue)
            {
                m_llEventsQueue.AddLast(irc_event);
            }
            return;
        }

        /// <summary>
        /// Handles IRC Client Status Messages
        /// </summary>
        /// <param name="message">Message to echo to status output</param>
        private void onStatusMessage(string message)
        {
            echo(message);
            return;
        }

        /// <summary>
        /// Handles IRC Client Active Message
        /// </summary>
        /// <param name="message">Message to echo to active output</param>
        private void onActiveMessage(string message)
        {
            //TODO: for now, we will echo this to status, but when we active channel windows, will echo this to whatever window is on top.
            echo(message);
            return;
        }

        /// <summary>
        /// Fires when IRC Event timer ticks
        /// </summary>
        private void onQueueProcessorTick(object sender, EventArgs args)
        {
            lock (m_llEventsQueue)
            {
                if (m_llEventsQueue.First != null)
                {
                    dispatch(m_llEventsQueue.First.Value);
                    m_llEventsQueue.Remove(m_llEventsQueue.First);
                }
            }
            return;
        }

        /// <summary>
        /// Fires when key is pressed in input area
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void txtInput_KeyPress(object sender, KeyPressEventArgs e)
        {
            if (e.KeyChar == '\r')
            {
                e.Handled = true;
                
                string input = txtInput.Text;
                txtInput.Clear();
                if (input.Length > 0)
                {
                    if (input[0] == '/')
                    {
                        if (input.Length > 1)
                        {
                            input = input.Substring(1); //If need be, the client will have to inform that we are not on a server, some commands dont require this...
                            m_client.send(input);
                        }
                    }
                    else
                    {
                        echo("You are not on any channel...");
                    }
                }
            }
            return;
        }

        #endregion

        #region [Helper Methods]

        /// <summary>
        /// Start Event Processor
        /// </summary>
        private void start_event_processor()
        {
            m_tmrQueueProcessor.Interval = 100;
            m_tmrQueueProcessor.Start();
        }

        private void end_event_processor()
        {
            m_tmrQueueProcessor.Stop();
        }

        /// <summary>
        /// dispatch IRC event action
        /// </summary>
        /// <param name="irc_event">IRC Event to act on</param>
        private void dispatch(IrcEvent irc_event)
        {

            IrcChannel chan = null;
            IrcUser user = null;
            string message = null;
            switch (irc_event.EventType)
            {
                case IrcEvents.server_connected:
                    echo("Connected!");
                    break;
                case IrcEvents.server_disconnected:
                    echo("Disconnected!");
                    break;
                case IrcEvents.server_registered:
                    echo("Registered");
                    break;
                case IrcEvents.private_message:
                    try
                    {
                        chan = (IrcChannel)irc_event.Arguments[IrcEventArguments.Channel];
                        user = (IrcUser)irc_event.Arguments[IrcEventArguments.User];
                        message = (string)irc_event.Arguments[IrcEventArguments.Message];
                        m_dictChannels[chan.Name].write(user, message);
                    }
                    catch (Exception)
                    {
                    }
                    break;
                case IrcEvents.channel_joined:
                    JoinChannel(irc_event);
                    break;
                case IrcEvents.channel_mode:
                    //TODO: Update event, or post something in the channel
                    break;
                case IrcEvents.channel_parted:
                    chan = (IrcChannel)irc_event.Arguments[IrcEventArguments.Channel];
                    if (!irc_event.Arguments.ContainsKey(IrcEventArguments.Source))
                    {
                        //TODO: Part Channel
                        m_dictChannels[chan.Name].Close();
                        m_dictChannels.Remove(chan.Name);
                    }
                    else
                    {
                        m_dictChannels[chan.Name].update_user_list();
                    }

                    break;
                case IrcEvents.channel_topic:

                    chan = (IrcChannel)irc_event.Arguments[IrcEventArguments.Channel];
                    m_dictChannels[chan.Name].Text = chan.Name + " [ " + irc_event.Arguments[IrcEventArguments.Topic].ToString() + " ] ";

                    break;
                case IrcEvents.notice:
                    echo("**notice**");
                    break;
            };

            return;
        }

        /// <summary>
        /// Join Channel Code
        /// </summary>
        /// <param name="irc_event">Join Channel Event</param>
        private void JoinChannel(IrcEvent irc_event)
        {
            IrcChannel channel = (IrcChannel)irc_event.Arguments[IrcEventArguments.Channel];

            if (!m_dictChannels.ContainsKey(channel.Name))
            {
                m_dictChannels[channel.Name]
                    = new ChannelForm(channel);
                m_dictChannels[channel.Name].Show();
            }
            else
            {
                m_dictChannels[channel.Name].update_user_list();
            }
        }

        /// <summary>
        /// Part channel
        /// </summary>
        /// <param name="irc_event"></param>
        private void PartChannel(IrcEvent irc_event)
        {
            //TODO: once this new event / signaling system is in place, the timer may just not be needed...
            //string channel_name = irc_event.Arguments[0];

            //if (!m_client.Channels.ContainsKey(channel_name))
            //{
            //    m_dictChannels[channel_name].Close();
            //    m_dictChannels.Remove(channel_name);
            //}
            //else
            //{
            //    m_dictChannels[channel_name].update_user_list();
            //}
        }

        /// <summary>
        /// Write text to the output
        /// </summary>
        /// <param name="message">message to 'echo' to the status output</param>
        private void echo(string message)
        {
            rtxtStatus.Text += message;
            rtxtStatus.Text += Environment.NewLine;

            rtxtStatus.Select(rtxtStatus.Text.Length, 1);
            rtxtStatus.ScrollToCaret();

            txtInput.Focus();

            return;
        }

        #endregion

        private void rtxtStatus_Enter(object sender, EventArgs e)
        {
            txtInput.Focus();
        }

    }
}
